package de.gameduell.net.src.connection {
	import de.gameduell.net.src.communication.MessageQueue;	
	
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	import de.gameduell.framework.util.LockManager;
	import de.gameduell.net.src.communication.ICommunication;
	import de.gameduell.net.src.communication.SyncEvent;
	import de.gameduell.net.src.message.IMessage;
	import de.gameduell.net.src.protocol.CommunicationRequests;	

	public class Synchronisation {	
		private static var instance:Synchronisation;
		
		private var communication:ICommunication;
		private var syncTimer:Timer;
		private var syncMessages:Array;
		private var syncedMessageNum:uint;
		private var currSyncId:int;
		private var lockManager:LockManager;
		private var attachedMessages:Boolean;

		public static function getInstance():Synchronisation{
			if(instance == null){
				 instance = new Synchronisation();
			}		
			return instance;
		}
		
		public function Synchronisation() {
			lockManager   = LockManager.getInstance();
			syncMessages  = new Array();
		}
		
		public function close():void{
			syncTimer.stop();
			syncTimer.removeEventListener(TimerEvent.TIMER, onSyncTimer);
			communication.removeEventListener(SyncEvent.ACK, onAck);
			communication.removeEventListener(SyncEvent.OUT_OF_ORDER_ACK, onOutOfOrderAck);
			communication = null;
			syncMessages  = new Array();
		}
		
		public function init(aCommunication:ICommunication,syncTime:int = 0):void{
			communication = aCommunication;
			
			if(syncTimer != null){	
				syncTimer.removeEventListener(TimerEvent.TIMER, onSyncTimer);
			}
			syncTimer = new Timer(syncTime, 0);
			syncTimer.addEventListener(TimerEvent.TIMER, onSyncTimer);
			communication.addEventListener(SyncEvent.ACK, onAck);
			communication.addEventListener(SyncEvent.OUT_OF_ORDER_ACK, onOutOfOrderAck);
		}
		
		public function stop():void{
			syncTimer.stop();
		}
		
		public function onReceiveMessage(event:SyncEvent):Boolean {
			lockManager.lock(this);
			for(var i:* in syncMessages){
				if(syncMessages[i] == event.messageId){
					lockManager.unlock(this);
					return false;	
				}
			}
			syncMessages.push(event.messageId);
			lockManager.unlock(this);
			attachedMessages = event.attachedMessages;
			if(event.attachedMessages){
				attachedMessages = true;
			}
			return true;
		}
		
		public function onOutOfOrderAck(event:SyncEvent):void{
			lockManager.lock(this);
			syncMessages.push(event.messageId);
			lockManager.unlock(this);
		}

		private function onAck(event:SyncEvent):void {
			syncMessages.splice(0,syncedMessageNum);
			
			if(attachedMessages){
				attachedMessages = false;
				sync();
			}else{	
				syncTimer.start();
			}
		}
		
		private function onSyncTimer(event:TimerEvent):void{
			var myQueue:MessageQueue = communication.getMessageQueue();
			if(myQueue.size == 0){
				sync();
			}
		}
		
		public function sync():void {
			syncTimer.stop();
			var message:IMessage = communication.send(CommunicationRequests.SYNC,[]);
			currSyncId = message.id;
		}
		
		public function updateSyncMessage(message:IMessage):IMessage {
			if(syncMessages.length > 0){
				message.parameters = new Array(syncMessages.join(","));
			}
			syncedMessageNum = syncMessages.length;	
			return message;		
		}
	}
}
